package com.adjy.pathbuilders.world;

import java.util.Random;

import org.jbox2d.dynamics.contacts.Contact;

import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.util.Log;

import com.adjy.pathbuilders.objects.DrinkEffect;
import com.adjy.pathbuilders.objects.GameEntityType;
import com.adjy.pathbuilders.objects.Thinkable;
import com.adjy.pathbuilders.physics.MovementType;
import com.adjy.pathbuilders.physics.PhysicalObject;
import com.adjy.pathbuilders.rendering.Animation;
import com.adjy.pathbuilders.rendering.AnimationPack;
import com.adjy.pathbuilders.screens.ShowdoorScreen;

// I think this class should be moved to the package objects
public class MainChar extends GameEntity implements Thinkable {
	
	public static final float[] SIZE = {6, 6};

	private Drawable deadFrame;
	private MovementType movementType, lastMovementType;
	private boolean onAdrenalinEffect;
	private int adrenalinEffectTime;

	private boolean drunk;
	private int drunkTime;
	/**
	 * A counter of frames used to determine the moment of choose a new movement
	 * to the MainChar.
	 */
	private int drunkChangeMovement;

	private Random random;
	private int lives;
	private int points;

	private StageProgress currentStageProgress;
	
	// TODO: Shouldnt this things be known only to class Adrenalin and Drink?
	// TODO: Remove the dependency on frames (change it to time [milliseconds])
	private static final int ADRENALIN_SPEED_FACTOR = 4;
	private static final int ADRENALIN_EFFECT_TIMEOUT = 5;
	private static final int DRUNK_CHANGE_MOVEMENT_TIME = 30; // Each Second
	private static final int DRUNK_TIMEOUT = 100; // Around 3 seconds ?

	public MainChar(int x, int y, AnimationPack ap) {
		// TODO add some checks
		super(GameEntityType.MAINCHAR, x, y, SIZE[0] - 3, SIZE[1] - 2 , ap, true, true, true);
		setRenderOffsets(-1.8f, 1.5f);
		setAnimation("mainchar_run_right");
		this.movementType = MovementType.MOVING_RIGHT;
		onAdrenalinEffect = false;
		adrenalinEffectTime = 0;
		drunkChangeMovement = 0;
		drunkTime = 0;
		random = new Random();
		this.lives = 1;
		this.points = 0;
		this.currentStageProgress = StageProgress.NOT_FINISHED;
		
	}
	
	public MovementType getMovementType() {
		return movementType;
	}

	public void setMovementType(MovementType movementType) {
		if(this.movementType.equals(movementType)) return;
		
		setLastMovementType(); // just do it if the movement is changed.
		this.movementType = movementType;
	}
	
	public MovementType getLastMovementType() {
		return lastMovementType;
	}
	
	private void setLastMovementType() {
		this.lastMovementType = this.movementType;
	}

	public void setDeadFrame(Drawable deadFrame) {
		this.deadFrame = deadFrame;
	}
	
	private void changeDirection() {
		switch (getMovementType()) {
		case MOVING_RIGHT:
			setMovementType(MovementType.MOVING_LEFT);
			break;
		case MOVING_LEFT:
			setMovementType(MovementType.MOVING_RIGHT);
		default:
			break;
		}
	}

	@Override
	public void hasStartedCollidingWith(PhysicalObject pObject, Contact contact) {
		// TODO add some argument checks
		GameEntity entity = (GameEntity) pObject;
		if (entity == null) {
			// Collided with Edge of the map
			changeDirection();
			return;
		}

		switch (entity.getType()) {
		case WALL:
			changeDirection();
			break;
		case APPLE:
			break;
		case BOX:
			break;
		case CLOUD:
			break;
		case FLOOR:
			break;
		case MAINCHAR:
			// Should never happen
			break;
		case ADRENALIN:
			onAdrenalin();
			break;
		case DRINK:
			drunk = true;
			break;

		case LIFE:
			// It's not here
			break;

		case FIRE:
			// It's not here
			break;

		case COIN:
			// It's not here
			break;

		default:
			break;
		}
	}

	private void onAdrenalin() {
		increaseSpeed();
		onAdrenalinEffect = true;
	}

	public void die() {
		kill();
	}

	@Override
	public void think(GameWorld gameWorld, float timePassed) {
		switch (getMovementType()) {
		case MOVING_RIGHT:
			setAnimation("mainchar_run_right");
			setXVelocity(10);
			break;
		case MOVING_LEFT:
			setAnimation("mainchar_run_left");
			setXVelocity(-10);
			break;
		case STOPPED:
			setAnimation("mainchar_idle_right");
			setXVelocity(0);
			break;
		default:
			break;
		}
		adrenalinEffect();
		drunkEffect();
	}

	private void adrenalinEffect() {
		if (onAdrenalinEffect) {
			increaseSpeed();
			manageAdrenalinEffect();
		}
	}

	private void manageAdrenalinEffect() {
		if (adrenalinEffectTime > ADRENALIN_EFFECT_TIMEOUT) {
			onAdrenalinEffect = false;
			adrenalinEffectTime = 0;
		} else {
			adrenalinEffectTime++;
		}
	}

	private void increaseSpeed() {
		setXVelocity(getXVelocity() * ADRENALIN_SPEED_FACTOR);
	}

	private void drunkEffect() {
		if (drunk) {
			drunkBehavior();
			manageDrunk();
		}
	}

	private void drunkBehavior() {
		drunkTime++;
		if (drunkChangeMovement >= DRUNK_CHANGE_MOVEMENT_TIME) {
			Log.i("Drink", "Randomizing Movement");
			drunkChangeMovement = 0;
			setMovementType(randomMovementType());
		} else {
			drunkChangeMovement++;
		}
	}

	private void manageDrunk() {
		if (drunkTime > DRUNK_TIMEOUT) {
			drunk = false;
			drunkTime = 0;
		}
	}

	// FIXME it would be better if we have a method
	// which receives an array and chooses randomly one
	// of its elements
	private MovementType randomMovementType() {
		int number = Math.abs(random.nextInt() % 2);

		if (number == 0) {
			return MovementType.MOVING_LEFT;
		} else {
			return MovementType.MOVING_RIGHT;
		}
	}

	public Drawable getDeadFrame() {
		return deadFrame;
	}

	public void gotOneLife() {
		setLives(lives + 1);
	}

	public void lostOneLife() {
		setLives(lives - 1);
	}

	public int getLives() {
		return lives;
	}

	public void setLives(int lives) {
		this.lives = lives;
		if (lives == 0) {
			this.die();
		}
	}

	public void gotPoints(int points) {
		this.points += points;
	}

	public int getPoints() {
		return points;
	}

	public void setCurrentStageProgress(StageProgress currentStageProgress) {
		this.currentStageProgress = currentStageProgress;
	}
	
	public StageProgress getCurrentStageProgress() {
		return currentStageProgress;
	}

		
	public boolean isDrunk(){
		return drunk;
	}
}

